package com.tnl.utils;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.tnl.app.entity.PayInfo;
import com.tnl.base.Const;
/**
 * 微信登陆认证comm
 * 
 * @author adela
 * 
 */
public class PayCommUtil {
	
    private static XStream xstream = new XStream(new XppDriver() {
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                //增加CDATA标记
                boolean cdata = true;
                @SuppressWarnings("rawtypes")
                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }
                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });

	public static String payInfoToXML(PayInfo pi) {
		xstream.alias("xml", pi.getClass());
		return xstream.toXML(pi);
	}
	
   public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            //多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = ip.indexOf(",");
            if(index != -1){
                return ip.substring(0,index);
            }else{
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            return ip;
        }
        return request.getRemoteAddr();
    }
   
   @SuppressWarnings("unchecked")
   public static Map<String, String> parseXml(String xml) throws Exception {
       Map<String, String> map = new HashMap<String, String>();
       Document document = DocumentHelper.parseText(xml);
       Element root = document.getRootElement();
       List<Element> elementList = root.elements();
       for (Element e : elementList)
           map.put(e.getName(), e.getText());
       return map;
   }
   

   /**
    * XML格式字符串转换为Map
    *
    * @param strXML XML字符串
    * @return XML数据转换后的Map
    * @throws Exception
    */
   public static Map<String, String> xmlToMap(String strXML) throws Exception {
       try {
           Map<String, String> data = new HashMap<String, String>();
           DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
           InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
           org.w3c.dom.Document doc = documentBuilder.parse(stream);
           doc.getDocumentElement().normalize();
           NodeList nodeList = doc.getDocumentElement().getChildNodes();
           for (int idx = 0; idx < nodeList.getLength(); ++idx) {
               Node node = nodeList.item(idx);
               if (node.getNodeType() == Node.ELEMENT_NODE) {
                   org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                   data.put(element.getNodeName(), element.getTextContent());
               }
           }
           try {
               stream.close();
           } catch (Exception ex) {
               // do nothing
           }
           return data;
       } catch (Exception ex) {
           WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
           throw ex;
       }

   }
   
   /**
    * 对字符串md5加密
    *
    * @param str
    * @return
    */
   public static String getMD5(String str) throws Exception {
       try {
           // 生成一个MD5加密计算摘要
           MessageDigest md = MessageDigest.getInstance("MD5");
           // 计算md5函数
           md.update(str.getBytes("UTF-8"));
           // digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
           // BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
           return new BigInteger(1, md.digest()).toString(16);
       } catch (Exception e) {
           throw new Exception("MD5加密出现错误");
       }
   }
   
   public static String getRandomOrderId() {
       // UUID.randomUUID().toString().replace("-","")
       Random random = new Random(System.currentTimeMillis());
       int value = random.nextInt();
       while (value < 0) {
           value = random.nextInt();
       }
       return value + "";
   }
   
   /**   
    * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串   
    * @param params 需要排序并参与字符拼接的参数组   
    * @return 拼接后字符串   
    */     
   public static String createLinkString(Map<String, String> params) {     
       List<String> keys = new ArrayList<String>(params.keySet());     
       Collections.sort(keys);     
       String prestr = "";     
       for (int i = 0; i < keys.size(); i++) {     
           String key = keys.get(i);     
           String value = params.get(key);     
           if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符     
               prestr = prestr + key + "=" + value;     
           } else {     
               prestr = prestr + key + "=" + value + "&";     
           }     
       }     
       return prestr;     
   } 
   
   /**   
    * 签名字符串   
    *  @param text需要签名的字符串   
    * @param sign 签名结果   
    * @param key密钥   
    * @param input_charset 编码格式   
    * @return 签名结果   
    */     
   public static boolean verify(String text, String sign, String key, String input_charset) {     
       text = text + key;     
       String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));     
       if (mysign.equals(sign)) {     
           return true;     
       } else {     
           return false;     
       }     
   }    
   
   /**   
    * @param content   
    * @param charset   
    * @return   
    * @throws SignatureException   
    * @throws UnsupportedEncodingException   
    */     
   public static byte[] getContentBytes(String content, String charset) {     
       if (charset == null || "".equals(charset)) {     
           return content.getBytes();     
       }     
       try {     
           return content.getBytes(charset);     
       } catch (UnsupportedEncodingException e) {     
           throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);     
       }     
   }  
   
   public final static String getMessageDigest(byte[] buffer) {
	      char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	      try {
	         MessageDigest mdTemp = MessageDigest.getInstance("MD5");
	         mdTemp.update(buffer);
	         byte[] md = mdTemp.digest();
	         int j = md.length;
	         char str[] = new char[j * 2];
	         int k = 0;
	         for (int i = 0; i < j; i++) {
	            byte byte0 = md[i];
	            str[k++] = hexDigits[byte0 >>> 4 & 0xf];
	            str[k++] = hexDigits[byte0 & 0xf];
	         }
	         return new String(str);
	      } catch (Exception e) {
	         return null;
	      }
	   }
	   private static String byteArrayToHexString(byte b[]) {
	        StringBuffer resultSb = new StringBuffer();  
	        for (int i = 0; i < b.length; i++)  
	            resultSb.append(byteToHexString(b[i]));  
	  
	        return resultSb.toString();  
	    }  
	  
	    private static String byteToHexString(byte b) {  
	        int n = b;  
	        if (n < 0)  
	            n += 256;  
	        int d1 = n / 16;  
	        int d2 = n % 16;  
	        return hexDigits[d1] + hexDigits[d2];  
	    }  
	  
	    public static String MD5Encode(String origin, String charsetname) {  
	        String resultString = null;
	        try {  
	            resultString = new String(origin);  
	            MessageDigest md = MessageDigest.getInstance("MD5");  
	            if (charsetname == null || "".equals(charsetname))  
	                resultString = byteArrayToHexString(md.digest(resultString  
	                        .getBytes()));  
	            else  
	                resultString = byteArrayToHexString(md.digest(resultString  
	                        .getBytes(charsetname)));  
	        } catch (Exception exception) {  
	        }  
	        return resultString;  
	    }  
	  
	    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",  
	        "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

	    /**
	     * @Author: KIKI
	     * @Description:支付参数生成签名
	     * @param characterEncoding
	     * @param parameters
	     * @Date: 2017-9-8 10:29
	     */
	    public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){  

	        StringBuffer sb = new StringBuffer();
	        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序（升序）
	        Iterator it = es.iterator();
	        while(it.hasNext()) {  
	            Map.Entry entry = (Map.Entry)it.next();  
	            String k = (String)entry.getKey();  
	            Object v = entry.getValue();  
	            if(null != v && !"".equals(v)&& !"sign".equals(k) && !"key".equals(k)) {
	                sb.append(k + "=" + v + "&");  
	            }
	        }  
	        sb.append("key=" + Const.APP_KEY);
	        String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();
	        return sign;  
	    }
}
